import {cloneDeep, isNumber, toNumber, isNaN, isArray, sumBy, sum} from 'lodash-es';
import {Decimal} from 'decimal.js';

type NumberLike = number | string;
type ObjectWithNumber = { [key: string]: number };
type NumericArray = (number | ObjectWithNumber)[];
type OperationType = '+' | '-' | '*' | '/' | 'sum';

/**
 * 使用 Decimal.js 进行精确算术，对数字或数字数组执行数学运算。
 *
 * @param {number | string | NumericArray} num1 - 第一个数字或数字数组。
 * @param {null | string | number} num2 - 第二个数字或数组的对象属性。
 * @param {'+' | '-' | '*' | '/' | 'sum'} operation - 要执行的操作：加法（'+'）、减法（'-'）、乘法（'*'）、除法（'/'）或数组求和（'sum'）。
 * @param {number} [digits=2] - 结果的小数位数（默认为 2）。
 * @returns {number} 指定精度的数学运算结果。
 *
 * @type {NumericArray} 包含数字或具有数字键的对象的数组。
 *
 * @example
 * // 加法
 * const result1 = numericalArithmetic(10, null, '+'); // 输出 10
 * console.log(result1);
 *
 * // 减法
 * const result2 = numericalArithmetic("2", "1", '-'); // 输出 1
 * console.log(result2);
 *
 * // 乘法
 * const result3 = numericalArithmetic(10.01000000000000000001, 5, '*'); // 输出 50.05
 * console.log(result3);
 *
 * // 除法
 * const result4 = numericalArithmetic(10, 2, '/'); // 输出 5
 * console.log(result4);
 *
 * // 计算数组中数字的和
 * const result5 = numericalArithmetic([4, 2, 8, 6], null, 'sum'); // 输出 20
 * console.log(result5);
 *
 * // 计算对象数组中指定属性值的和
 * const result6 = numericalArithmetic([{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }], 'n', 'sum'); // 输出 20
 * console.log(result6);
 *
 * @throws {Error} 如果操作是 'sum' 并且输入数据格式不正确，则抛出错误。
 * @throws {Error} 如果除数为 0 则抛出错误。
 * @throws {Error} 如果不支持操作，则抛出错误。
 * @throws {Error} 如果操作数的类型不正确，则抛出错误。
 */
export function numericalArithmetic(num1: NumberLike | NumericArray, num2: NumberLike | null, operation: OperationType, digits: number = 2): number {
  let num: Decimal;
  let n1: Decimal | NumberLike | NumericArray = num1;
  let n2: Decimal | NumberLike | null = num2;

  // 辅助函数：计算数组中数字的和
  const calculateSum = (data: NumericArray, key?: string): Decimal => {
    const arr = cloneDeep(data);
    for (let i = 0; i < arr.length; i++) {
      if (key) {
        if (!isNumber((arr[i] as { [key: string]: number })[key])) {
          (arr[i] as { [key: string]: number })[key] = toNumber((arr[i] as { [key: string]: number })[key]);
        }
        if (isNaN((arr[i] as { [key: string]: number })[key])) {
          (arr[i] as { [key: string]: number })[key] = 0;
        }
      } else {
        if (!isNumber(arr[i])) {
          arr[i] = toNumber(arr[i]);
        }
        if (isNaN(arr[i])) {
          arr[i] = 0;
        }
      }
    }
    if (key) {
      return new Decimal(toNumber(sumBy(arr, key)) || 0);
    } else {
      return new Decimal(toNumber(sum(arr)) || 0);
    }
  };

  // 转换为数字或者处理数组
  if (operation === 'sum' && !isArray(n1)) {
    throw new Error('sum 数据格式不正确');
  } else if (operation === '+' || operation === '-' || operation === '*' || operation === '/') {
    n1 = new Decimal(toNumber(n1) || 0);
    n2 = new Decimal(toNumber(n2) || 0);
  }

  // 执行操作
  switch (operation) {
    case '+':
      num = (n1 as Decimal).plus(n2 as Decimal);
      break;
    case '-':
      num = (n1 as Decimal).minus(n2 as Decimal);
      break;
    case '*':
      num = (n1 as Decimal).times(n2 as Decimal);
      break;
    case '/':
      if ((n2 as Decimal).isZero()) {
        throw new Error('除数不能为0');
      }
      num = (n1 as Decimal).dividedBy(n2 as Decimal);
      break;
    case 'sum':
      num = calculateSum(n1 as NumericArray, n2 as unknown as string);
      break;
    default:
      throw new Error('mathNums 不支持的计算');
  }

  // 返回结果，确保精度为指定的小数位数
  return num.toDecimalPlaces(digits).toNumber();
}

export default numericalArithmetic;
